package org.web.accesscontrollist.server.biz.impl;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.web.accesscontrollist.sdk.biz.AclResourceUserBindingBiz;
import org.web.accesscontrollist.sdk.dto.*;
import org.web.accesscontrollist.sdk.dto.query.QueryAclResourceUserBindingRequest;
import org.web.accesscontrollist.sdk.helper.AclHelper;
import org.web.accesscontrollist.server.domain.*;
import org.web.accesscontrollist.server.domain.AclResourceUserBindingDO;
import org.web.accesscontrollist.server.domain.query.QueryAclResourceDetail;
import org.web.accesscontrollist.server.domain.query.QueryAclResourceUserBinding;
import org.web.accesscontrollist.server.service.AclResourceDetailService;
import org.web.accesscontrollist.server.service.AclResourceUserBindingService;
import org.web.base.domain.*;
import org.web.base.domain.exception.ResultMessageEnum;
import org.web.base.domain.exception.ServiceException;
import org.web.base.domain.helper.ResultHelper;
import org.web.base.domain.helper.ServiceExceptionHelper;
import org.web.base.domain.helper.ValidationHelper;

import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.*;
import java.util.stream.Collectors;


@Service("aclResourceUserBindingBiz")
public class AclResourceUserBindingBizImpl implements AclResourceUserBindingBiz {

    private static final Logger logger = Logger.getLogger(AclResourceUserBindingBizImpl.class);

    @Resource
    private AclResourceUserBindingService aclResourceUserBindingService;

    @Resource
    private AclResourceDetailService aclResourceDetailService;
    private static final Set<String> queryDetailSet = new HashSet<String>() {{
        add("acl_resource_detail_id");
        add("acl_resource_type_id");
        add("business_line");
        add("type_name");
        add("detail_name");
        add("resource_level");
        add("first_level");
        add("second_level");
        add("third_level");
        add("fourth_level");
        add("fifth_level");
    }};

    private static final Set<String> querySimpleSet = new HashSet<String>() {{
        add("acl_resource_user_binding_id");
        add("business_line");
        add("type_name");
        add("detail_name");
        add("user_num");
        add("role_name");
        add("create_time");
        add("is_delete");
    }};


    @Override
    public ResultDO<List<AclResourceUserBindingDTO>> saveAclResourceUserBindingBatch(AclResourceUserBindingBatchDTO aclResourceUserBindingBatchDTO) {
        ResultDO<List<AclResourceUserBindingDTO>> resultDO = new ResultDO<>();
        OperatorDTO operatorDTO = null;
        try {
            AclHelper.checkSaveAclResourceUserBindingBatchDTO(aclResourceUserBindingBatchDTO, UpdateGroup.class);
            resultDO.setDomain(aclResourceUserBindingBatchDTO.getAclResourceUserBindingDTOList());
            // 判断业务线businessLine是否存在, 则校验权限。
            operatorDTO = aclResourceUserBindingBatchDTO.getOperatorDTO();
            // aclBusinessLineServiceBiz.judgeOperatorOrBuildDefaultBusinessLine(operatorDTO);

            // 根据typeNameList，detailNameList和businessLine,通过一次查询，查出数据库所有的数据。不在循环中访问数据库，减少数据库操作次数。
            List<String> typeNameList = new ArrayList<>();
            List<String> detailNameList = new ArrayList<>();
            Map<String, AclResourceUserBindingDTO> resourceUserBindingDTOMap = new HashMap<>();
            List<String> typeNameAndDetailNameList = new ArrayList<>();
            for (AclResourceUserBindingDTO aclResourceUserBindingDTO : aclResourceUserBindingBatchDTO.getAclResourceUserBindingDTOList()) {
                AclHelper.addIfNotDuplicate(typeNameList, aclResourceUserBindingDTO.getTypeName());
                AclHelper.addIfNotDuplicate(detailNameList, aclResourceUserBindingDTO.getDetailName());
                typeNameAndDetailNameList.add(buildTypeNameAndDetailName(aclResourceUserBindingDTO.getTypeName(), aclResourceUserBindingDTO.getDetailName()));
                String keyName = buildUniqueKeyByAclResourceUserBindingDTO(aclResourceUserBindingDTO);
                resourceUserBindingDTOMap.put(keyName, aclResourceUserBindingDTO);
            }

            // 数据依赖关系校验，ResourceDetail依赖 ResourceType,根据businessLine、typeName校验ResourceType是否存在。提交批次中的businessLine、typeName在acl_resource_type表都应该存在。
            List<String> errorList = new ArrayList<>();
            setDetailInfo2BindingAndCheckDetailInfoExist(typeNameList, detailNameList, typeNameAndDetailNameList, errorList, aclResourceUserBindingBatchDTO);

            QueryAclResourceUserBinding queryAclResourceUserBinding = new QueryAclResourceUserBinding();
            queryAclResourceUserBinding.setAllRecords();
            queryAclResourceUserBinding.setBusinessLine(aclResourceUserBindingBatchDTO.getOperatorDTO().getBusinessLine());
            queryAclResourceUserBinding.setTypeNameList(typeNameList);
            queryAclResourceUserBinding.setDetailNameList(detailNameList);
            queryAclResourceUserBinding.setSpecifiedAttributeSet(querySimpleSet);
            List<AclResourceUserBindingDO> selectListFromDB = aclResourceUserBindingService.selectAclResourceUserBindingList(queryAclResourceUserBinding);

            Map<String, AclResourceUserBindingDO> mapFromDB = new HashMap<>();
            for (AclResourceUserBindingDO aclResourceUserBindingDO : selectListFromDB) {
                String keyName = buildUniqueKeyByAclResourceUserBindingDO(aclResourceUserBindingDO);
                mapFromDB.put(keyName, aclResourceUserBindingDO);
            }

            List<AclResourceUserBindingDO> insertList = new ArrayList<>();// 新增数据队列
            List<AclResourceUserBindingDO> updateList = new ArrayList<>();// 修改数据队列

            int count = 0;
            for (AclResourceUserBindingDTO aclResourceUserBindingDTO : aclResourceUserBindingBatchDTO.getAclResourceUserBindingDTOList()) {
                ++count;
                String keyName = buildUniqueKeyByAclResourceUserBindingDTO(aclResourceUserBindingDTO);
                AclResourceUserBindingDO aclResourceUserBindingDO = mapFromDB.get(keyName);
                if (aclResourceUserBindingDO == null) {
                    AclResourceUserBindingDO insertAclResourceUserBindingDO = new AclResourceUserBindingDO();
                    BeanUtils.copyProperties(insertAclResourceUserBindingDO, aclResourceUserBindingDTO);
                    // insert的场合，校验下数据合法性。
                    AclHelper.validate(count, errorList, AclResourceUserBindingDTO.class, aclResourceUserBindingDTO, InsertGroup.class);
                    insertList.add(insertAclResourceUserBindingDO);
                } else {
                    AclResourceUserBindingDO updateAclResourceUserBindingDO = new AclResourceUserBindingDO();
                    BeanUtils.copyProperties(updateAclResourceUserBindingDO, aclResourceUserBindingDTO);
                    // update的场合，不需要校验数据合法性。原因是前面的 AclHelper.checkSaveAclDetailBatchDTO(aclResourceTypeBatchDTO)已经校验。
                    updateAclResourceUserBindingDO.setAclResourceDetailId(aclResourceUserBindingDTO.getAclResourceDetailId());
                    updateAclResourceUserBindingDO.setAclResourceUserBindingId(aclResourceUserBindingDO.getAclResourceUserBindingId());
                    updateList.add(updateAclResourceUserBindingDO);
                }
            }

            if (!errorList.isEmpty()) {
                ServiceException serviceException = ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID);
                serviceException.setErrorList(errorList);
                throw serviceException;
            }

            // 最后进行数据更新。
            for (AclResourceUserBindingDO aclResourceUserBindingDO : insertList) {
                aclResourceUserBindingService.insertAclResourceUserBinding(aclResourceUserBindingDO);
                supplementReturn(resourceUserBindingDTOMap, mapFromDB, aclResourceUserBindingDO);
            }

            for (AclResourceUserBindingDO aclResourceUserBindingDO : updateList) {
                aclResourceUserBindingService.updateAclResourceUserBindingByAclResourceUserBindingId(aclResourceUserBindingDO);
                supplementReturn(resourceUserBindingDTOMap, mapFromDB, aclResourceUserBindingDO);
            }

        } catch (Exception e) {
            logger.error(ServiceExceptionHelper.getExceptionInfo(e));
            resultDO = ResultHelper.buildResultDOByException(e, resultDO.getDomain());
        } finally {
            if (operatorDTO != null) {
                operatorDTO.setAccessKey(null);
            }
        }
        return resultDO;
    }

    @Override
    public ResultDO<List<AclResourceUserBindingDTO>> selectAclResourceUserBindingList(QueryAclResourceUserBindingRequest queryAclResourceUserBindingRequest) {
        ResultDO<List<AclResourceUserBindingDTO>> resultDO = new ResultDO<>();
        try {
            ValidationHelper.validateWithThrow(QueryAclResourceUserBindingRequest.class, queryAclResourceUserBindingRequest);
            QueryAclResourceUserBinding queryAclResourceUserBinding = new QueryAclResourceUserBinding();
            BeanUtils.copyProperties(queryAclResourceUserBinding, queryAclResourceUserBindingRequest);
            ResultDO<List<AclResourceUserBindingDO>> selectResult = aclResourceUserBindingService.selectAclResourceUserBindingListResult(queryAclResourceUserBinding);
            if (selectResult.isSuccess()) {
                List<AclResourceUserBindingDTO> list = new ArrayList<>();
                resultDO.setDomain(list);
                for (AclResourceUserBindingDO aclResourceUserBindingDO : selectResult.getDomain()) {
                    AclResourceUserBindingDTO aclResourceUserBindingDTO = new AclResourceUserBindingDTO();
                    BeanUtils.copyProperties(aclResourceUserBindingDTO, aclResourceUserBindingDO);
                    list.add(aclResourceUserBindingDTO);
                }
                resultDO.setTotalCount(selectResult.getTotalCount());
            } else {
                BeanUtils.copyProperties(resultDO, selectResult);
            }
        } catch (Exception e) {
            logger.error(ServiceExceptionHelper.getExceptionInfo(e));
            resultDO = ResultHelper.buildResultDOByException(e);
        }
        return resultDO;
    }

    private void supplementReturn(Map<String, AclResourceUserBindingDTO> resourceUserBindingDTOMap, Map<String, AclResourceUserBindingDO> mapFromDB, AclResourceUserBindingDO aclResourceUserBindingDO) {
        String keyName = buildUniqueKeyByAclResourceUserBindingDO(aclResourceUserBindingDO);
        AclResourceUserBindingDTO aclResourceUserBindingDTO = resourceUserBindingDTOMap.get(keyName);
        aclResourceUserBindingDTO.setExecuteSuccess(true);
        AclResourceUserBindingDO fromDB = mapFromDB.get(keyName);
        if (fromDB != null) {
            aclResourceUserBindingDTO.setCreateTime(fromDB.getCreateTime());
            aclResourceUserBindingDTO.setIsDelete(fromDB.getIsDelete());
        } else {
            aclResourceUserBindingDTO.setCreateTime(aclResourceUserBindingDO.getCreateTime());
            aclResourceUserBindingDTO.setIsDelete(aclResourceUserBindingDO.getIsDelete());
        }
        aclResourceUserBindingDTO.setUpdateTime(aclResourceUserBindingDO.getUpdateTime());
    }

    /**
     * (1)数据依赖关系校验，绑定关系与ResourceDetail之间的依赖,根据businessLine、typeName和detailName锁定的记录，在【acl_resource_user_binding】是否存在。提交批次中的businessLine、typeName和detailName组合信息在acl_resource_detail表都应该存在。
     * (2)根据ResourceDetail信息，为AclResourceUserBindingDTO设置resourceDetail、resourceTypeId、resourceLevel、firstLevel、secondLevel、thirdLevel、fourthLevel、fifthLevel。
     */
    private void setDetailInfo2BindingAndCheckDetailInfoExist(List<String> typeNameList, List<String> detailNameList, List<String> typeNameAndDetailNameList, List<String> errorList, AclResourceUserBindingBatchDTO aclResourceUserBindingBatchDTO) {
        // 数据依赖关系开始，ResourceDetail依赖 ResourceType,根据businessLine、typeName校验ResourceType是否存在。提交批次中的businessLine、typeName在acl_resource_type表都应该存在。
        QueryAclResourceDetail queryAclResourceDetail = new QueryAclResourceDetail();
        queryAclResourceDetail.setBusinessLine(aclResourceUserBindingBatchDTO.getOperatorDTO().getBusinessLine());
        queryAclResourceDetail.setAllRecords();
        queryAclResourceDetail.setTypeNameList(typeNameList);
        queryAclResourceDetail.setDetailNameList(detailNameList);
        queryAclResourceDetail.setSpecifiedAttributeSet(queryDetailSet);
        List<AclResourceDetailDO> selectAclResourceDetailDOListFromDB = aclResourceDetailService.selectAclResourceDetailList(queryAclResourceDetail);

        Map<String, AclResourceDetailDO> selectResourceDetailMapFromDB = new HashMap<>();
        List<String> typeNameAndDetailNameListFromDB = new ArrayList<>();
        for (AclResourceDetailDO aclResourceDetailDO : selectAclResourceDetailDOListFromDB) {
            String typeNameAndDetailName = buildTypeNameAndDetailName(aclResourceDetailDO.getTypeName(), aclResourceDetailDO.getDetailName());
            typeNameAndDetailNameListFromDB.add(typeNameAndDetailName);
            selectResourceDetailMapFromDB.put(typeNameAndDetailName, aclResourceDetailDO);
        }

        List<String> difference = typeNameAndDetailNameList.stream().filter(item -> !typeNameAndDetailNameListFromDB.contains(item)) // 筛选出不在 typeNameAndDetailNameListFromDB 中typeNameAndDetailName组合元素。
                .collect(Collectors.toList()); // 将结果收集到一个新的 List 中
        if (!difference.isEmpty()) {
            errorList.add("依赖的资源详情不存在，不存在的typeNameAndDetailName组合元素列表" + String.join(",", difference));
            throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.DATA_LOSE_EFFICACY, errorList);
        }

        // 根据ResourceType信息，为AclResourceUserBindingDTO设置resourceDetail、resourceTypeId、resourceLevel、firstLevel、secondLevel、thirdLevel、fourthLevel、fifthLevel
        for (AclResourceUserBindingDTO aclResourceUserBindingDTO : aclResourceUserBindingBatchDTO.getAclResourceUserBindingDTOList()) {
            String typeNameAndDetailName = buildTypeNameAndDetailName(aclResourceUserBindingDTO.getTypeName(), aclResourceUserBindingDTO.getDetailName());
            AclResourceDetailDO aclResourceDetailDO = selectResourceDetailMapFromDB.get(typeNameAndDetailName);
            aclResourceUserBindingDTO.setAclResourceTypeId(aclResourceDetailDO.getAclResourceTypeId());
            aclResourceUserBindingDTO.setAclResourceDetailId(aclResourceDetailDO.getAclResourceDetailId());
            aclResourceUserBindingDTO.setResourceLevel(aclResourceDetailDO.getResourceLevel());
            aclResourceUserBindingDTO.setFirstLevel(aclResourceDetailDO.getFirstLevel());
            aclResourceUserBindingDTO.setSecondLevel(aclResourceDetailDO.getSecondLevel());
            aclResourceUserBindingDTO.setThirdLevel(aclResourceDetailDO.getThirdLevel());
            aclResourceUserBindingDTO.setFourthLevel(aclResourceDetailDO.getFourthLevel());
            aclResourceUserBindingDTO.setFifthLevel(aclResourceDetailDO.getFifthLevel());
        }
        // 数据依赖关系结束
    }

    private String buildTypeNameAndDetailName(String typeName, String detailName) {
        return typeName + "-" + detailName;
    }

    private String buildUniqueKeyByAclResourceUserBindingDTO(AclResourceUserBindingDTO aclResourceUserBindingDTO) {
        return AclHelper.buildUniqueKeyByAttributes(aclResourceUserBindingDTO.getBusinessLine(), aclResourceUserBindingDTO.getTypeName(), aclResourceUserBindingDTO.getDetailName(), aclResourceUserBindingDTO.getUserNum(), aclResourceUserBindingDTO.getRoleName());
    }

    private String buildUniqueKeyByAclResourceUserBindingDO(AclResourceUserBindingDO aclResourceUserBindingDO) {
        return AclHelper.buildUniqueKeyByAttributes(aclResourceUserBindingDO.getBusinessLine(), aclResourceUserBindingDO.getTypeName(), aclResourceUserBindingDO.getDetailName(), aclResourceUserBindingDO.getUserNum(), aclResourceUserBindingDO.getRoleName());
    }
}
